构造函数 默认构造函数
构造函数是为了保证对象的每个数据成员都被正确初始化,通常情况下构造函数应声明为公有函数
,一般被隐式地调用。构造函数被声明为私有有特殊的用途,比如单例模式
栈上的对象
生存期到了会自动调用析构函数
;
new operator做了两件事,一个是创建了对象内存,一个是调用构造函数;堆上的内存需要delete释放
,做了两件事,一是调用析构函数,二是释放内存。堆上new的对象,不delete的话,不会自动调用析构函数
#ifndef _TEST_H_
#define _TEST_H_
class Test
{
public:
// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的
// 默认的构造函数
Test();
Test(int num);
void Display();
Test &operator=(const Test &other);
~Test();
private:
int num_;
};
#endif // _TEST_H_
#include "Test.h"
#include <iostream>
using namespace std;
// 不带参数的构造函数称为默认构造函数
Test::Test()
{
num_ = 0;
cout << "Initializing Default" << endl;
}
Test::Test(int num)
{
num_ = num;
cout << "Initializing " << num_ << endl;
}
Test::~Test()
{
cout << "Destroy " << num_ << endl;
}
void Test::Display()
{
cout << "num=" << num_ << endl;
}
Test &Test::operator=(const Test &other)
{
cout << "Test::operator=" << endl;
if (this == &other)
return *this;
num_ = other.num_;
return *this;
}
全局对象
的构造先于main
函数执行,在return 0时
全局变量的生存期也到了,故也会自动调用析构函数
。
#include "Test.h"
#include <iostream>
using namespace std;
Test t(10);
int main(void)
{
cout << "Entering main ..." << endl;
cout << "Exiting main ..." << endl;
return 0;
}
运行结果
Initializing 10
Entering main ...
Exiting main ...
Destroy 10
默认构造函数
不带参数的构造函数称为默认构造函数
如果程序中未声明,则系统自动产生出一个默认构造函数
,是空函数
如果程序实现任何一个构造函数(包括拷贝构造函数),那么编译器将不再提供默认构造函数
int main(void)
{
Test t;
t.Display();
Test t2(10);
t2.Display();
Test *t3 = new Test(20); // new operator
t3->Display();
delete t3;
return 0;
}
运行结果
Initializing Default
num=0
Initializing 10
num=10
Initializing 20
num=20
Destroy 20
Destroy 10
Destroy 0
转换构造函数
将其它类型转换为类类型
,类的构造函数只有一个参数是非常危险
的,因为编译器可以使用这种构造函数把参数的类型隐式转换为类类型
单个参数的构造函数不一定是转换构造函数
#include "Test.h"
int main(void)
{
Test t(10); //
带一个参数的构造函数,充当的是普通构造函数的功能
t = 20; // 将20这个整数赋值给t对象
// 1、调用转换构造函数将20这个整数转换成类类型 (生成一个临时对象)
// 2、将临时对象赋值给t对象(调用的是=运算符)
Test t2;
return 0;
}
运行结果
Initializing 10
Initializing 20
Test::operator=
Destroy 20
Initializing Default
Destroy 0
Destroy 20
调用转换构造函数将20这个整数转换成类类型,生成一个临时对象,然后调用赋值运算符operator=,然后释放临时对象
。
Test t[2] = {10, 20}; 中10,20是当作参数传递给每个对象的构造函数
的。如果没有对应的构造函数,比如只有2个参数的构造函数,那么编译是失败的
赋值与初始化的区别
在初始化语句中的等号不是运算符
#include "Test.h"
int main(void)
{
Test t = 10; // 等价于Test t(10); 这里的=不是运算符,表示初始化。
t = 20; // 赋值操作
Test t2;
t = t2; // 赋值操作 t.operator=(t2);
return 0;
}
运行结果
Initializing 10
Initializing 20
Test::operator=
Destroy 20
Initializing Default
Test::operator=
Destroy 0
Destroy 0
explicit关键字
只提供给类的构造函数使用
的关键字。编译器不会把声明为explicit的构造函数用于隐式转换,它只能在程序代码中显示创建对象
假设在Test类的构造函数Test(int num); 前面加上explicit关键字,那么Test t = 10; 或者 t = 20;这种语句都是编译不通过的,因为不允许隐式转换。